<?php
namespace Kuoqi;

use Kuoqi\Worker\Signal;//信号处理器
#stream_context_create
#ob_implicit_flush
#Sockets
/**
 * Class worker
 * @package Kuoqi
 * User kuoqi
 * Datetime 2022/3/17 9:03
 * Remark worker 基础信息，当然这个是要用cli模式才能运行的喔
 * php worker.php restart
 * netstat -tlnp|grep 6519
 */
class Worker{

    /**
     * 配置pid文件相关信息
     * @var string
     * User kuoqi
     * Datetime 2022/3/27 11:25
     * Remark
     */
    protected $pidfile = '';

    /**
     * 进程uid
     * @var int
     * User kuoqi
     * Datetime 2022/3/27 11:26
     * Remark
     */
    protected $uid        = 80;

    /**
     * 进程GID
     * @var int
     * User kuoqi
     * Datetime 2022/3/27 11:26
     * Remark
     */
    protected $gid        = 80;

    /**
     * 当前命令信息
     * @var string
     * User kuoqi
     * Datetime 2022/3/27 11:45
     * Remark
     */
    protected $command = '';
    /**
     * 当前主脚本
     * @var string
     * User kuoqi
     * Datetime 2022/3/27 11:48
     * Remark
     */
    protected $index = '';

    /**
     * 守护进程 时间戳
     * @var int
     * User kuoqi
     * Datetime 2022/3/27 11:57
     * Remark
     */
    protected $sleep    = 5;

    /**
     * 时间间隔
     * @var int
     * User kuoqi
     * Datetime 2022/3/31 11:33
     * Remark
     */
    protected $interval = 1;

    /**
     * 输入参数初始化
     * @var bool
     * User kuoqi
     * Datetime 2022/3/29 19:53
     * Remark
     */
    protected $argv_init = false;

    public function __construct()
    {
        //配置pid文件信息
        $cls = strtolower(str_replace(\Kuoqi\Params::$RL,'_',get_called_class()));
        $this->pidfile = '/var/run/'.basename($cls, '.php').'.pid';
        //监听信号
        $this->signal();
    }

    public function __destruct()
    {
        // TODO: Implement __destruct() method.
        $this->say('等待子进程中断，防止子进程成为僵尸进程。');
        pcntl_wait($status); //等待子进程中断，防止子进程成为僵尸进程。
    }

    //主进程
    public function main(){
        if($this->command === 'stop'){
            $this->stop();
        }else if($this->command === 'start'){
            $this->start();
        }else if($this->command === 'restart'){
            $this->stop();
            $this->start();
        }else if($this->command === 'status'){
            $this->status();
        }else if($this->command === 'foreground'){
            $this->foreground();
        }else{
            $this->help($this->index);
        }
    }

    //当前worker的进程执行
    public function run(){
        $this->say('do something for worker with worker->run..');
    }

    //run运行进程
    private function runProcess(){
        while($this->checkPid()){
            $this->say("The process begin.");
            while($this->checkPid()){
                pcntl_signal_dispatch();
                $this->run();
                if($this->interval){
                    $this->say('do something for worker with worker->run...');
                    sleep($this->interval);
                }
                //$this->say('do something');
                if(Signal::get() == SIGHUP){
                    Signal::reset();
                    break;
                }
            }
            printf("\n");
            $this->say("The process end.");

        }
    }

    //开始进程
    private function start(){
        $pid = $this->daemon();
        $this->say("");
        $this->say('************************************************************************');
        $this->say('******  KuoqiWorker  welcome                              **************');
        $this->say('******  create by kuoqi with just php use pcntl_fork      **************');
        $this->say('************************************************************************');
        //启动一个worker
        $this->say('KuoqiWorker start');
        while($this->checkPid()){
            $this->runProcess();
            sleep($this->sleep);
        }
    }

    /**
     * 状态监测
     * User kuoqi
     * Datetime 2022/3/27 12:01
     * Remark
     */
    private function status(){
        $this->say('check status');
        if ($this->checkPid()) {
            $pid = file_get_contents($this->pidfile);
            system(sprintf("ps ax | grep %s | grep -v grep", $pid));
        }else{
            $this->say('no pid with :'.$this->pidfile);
        }
    }

    //停止进程
    private function stop(){
        $this->say('stop worker..');
        if ($this->checkPid()) {
            $pid = file_get_contents($this->pidfile);
            posix_kill($pid, 0);
            unlink($this->pidfile);
        }
    }

    //重启进程
//    private function restart(){
//        if ($this->checkPid()) {
//            $pid = file_get_contents($this->pidfile);
//            //posix_kill(posix_getpid(), SIGHUP);
//            posix_kill($pid, SIGHUP);
//        }
//    }

    /**
     * 检测pid的相关信息，如果是,并返回相关数据信息
     * @return bool
     * User kuoqi
     * Datetime 2022/3/30 19:50
     * Remark
     */
    private function checkPid(){
        clearstatcache(true,$this->pidfile);
        return file_exists($this->pidfile);
    }

    /**
     * 守护进程
     * @return int
     * User kuoqi
     * Datetime 2022/3/30 19:46
     * Remark
     */
    private function daemon(){
        if (file_exists($this->pidfile)) {
            echo "The file $this->pidfile exists.\n";
            exit();
        }

        $pid = pcntl_fork();
        if ($pid == -1) {
            die('could not fork');
        } else if ($pid) {
            // we are the parent
            //pcntl_wait($status); //Protect against Zombie children
            exit($pid);
        } else {
            file_put_contents($this->pidfile, getmypid());
            posix_setuid($this->uid);
            posix_setgid($this->gid);
            return(getmypid());
        }
    }


    /**
     * 信号进程
     * User kuoqi
     * Datetime 2022/3/27 11:28
     * Remark
     */
    private function signal(){
        pcntl_signal(SIGHUP,  function($signo) /*use ()*/{
            //echo "\n This signal is called. [$signo] \n";
            printf("The process has been reload.\n");
            Signal::set($signo);
        });
    }

    private function foreground(){
        $this->runProcess();
    }

    /**
     * 帮助方法
     * @param $proc
     * User kuoqi
     * Datetime 2022/3/27 11:50
     * Remark
     */
    private function help($proc){
        $this->say(sprintf("%s start | stop | restart | status | foreground | help \n", $proc));
    }


    //输出内容模块
    protected function say($msg = ""){
        echo $msg . PHP_EOL;
    }

    /**
     * 初始化
     * User kuoqi
     * Datetime 2022/3/29 19:54
     * Remark
     */
    public function initInput(){
        if($this->argv_init){ return ;}
        global $argv;
        if(count($argv) < 2){
            $this->help($argv[0]);
            printf("please input help parameter\n");
            exit();
        }
        $this->index = $argv[0];
        $this->command = $argv[1];
        $this->argv_init = true;
    }

    /**
     * User kuoqi
     * Datetime 2022/3/27 11:36
     * Remark 正式运行主体
     */
    public function runAll(){
        $this->initInput();
        $this->main();
    }
}
